package com.xd.core.h5.controller;

import com.alibaba.fastjson.JSONObject;
import com.ancun.netsign.utils.RsaEncrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Maps;
import com.xd.common.ascept.CheckH5Url;
import com.xd.common.ascept.CheckSign;
import com.xd.common.constant.SysConstant;
import com.xd.common.exception.XkdException;
import com.xd.common.properties.AqHtProperties;
import com.xd.common.util.*;
import com.xd.core.api.dto.ConfirmSignDto;
import com.xd.core.api.dto.ReadySignDto;
import com.xd.core.api.service.ApiContractService;
import com.xd.core.api.service.DLApiService;
import com.xd.core.business.dto.InsuranceDto;
import com.xd.core.business.entity.Contract;
import com.xd.core.business.entity.Credit;
import com.xd.core.business.entity.IncomingInfo;
import com.xd.core.business.mapper.ContractMapper;
import com.xd.core.business.mapper.IncomingInfoMapper;
import com.xd.core.business.service.ICreditService;
import com.xd.core.business.vo.IncomingInfoInsuranceVo;
import com.xd.core.h5.dto.AdvancePaymentDto;
import com.xd.core.h5.vo.AddSignerVo;
import com.xd.core.h5.vo.AdvanceRepaymentDetail;
import com.xd.core.h5.vo.CarInsuranceDetail;
import com.xd.core.h5.vo.InsuranceIncomingDetailVo;
import com.xd.core.note.IAsyncService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/*
 * 进件H5
 * 点击生成二维码
 * 调用人脸识别-生成人脸识别链接，认证完成跳转页面，如果是认证成功，是企业生成印章
 * 认证成功生成个人信息授权书
 * 完成进件-等待授信状态
 * */
@Slf4j
@Api(tags = "h5")
@RestController
@RequestMapping("/core/h5")
public class H5Controller {

    @Autowired
    private IncomingInfoMapper incomingInfoMapper;
    @Autowired
    private AqHtProperties aqHtProperties;
    @Autowired
    private ContractMapper contractMapper;
    @Value("${h5rsakey.privateKey}")
    private String privateKey;
    @Value("${image.contract}")
    private String contractUrl;
    @Value("${image.localPathDir}")
    private String localPathDir;
    @Autowired
    private ICreditService iCreditService;
    @Resource
    private DLApiService dlApiService;
    @Resource
    private ApiContractService apiContractService;
    @Value("${spring.profiles.active}")
    private String active;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    private static final String H5_LINK = "H5_LINK:";//H5链接
    private static final String serialNo = "serialNo";//流水号
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    @Qualifier("delayedScheduler")
    private ScheduledExecutorService delayedScheduler;
    private static final String ADVANCE_LOCK_KEY = "lock_advance";//预付款分布式锁


    @CheckH5Url
    @ApiOperation("保单贷--待签约详情")
    @PostMapping("/getInsuranceDetail")
    public InsuranceIncomingDetailVo getInsuranceDetail(HttpServletRequest request) {
        JSONObject jsonObject = checkToken(request);
        Integer incomingId = jsonObject.getInteger("incomingId");
        return incomingInfoMapper.getInsuranceDetail(incomingId);
    }

    @CheckH5Url
    @ApiOperation("保单贷--车险分期")
    @PostMapping("/goContract")
    public CarInsuranceDetail goContract(HttpServletRequest request) {
        JSONObject jsonObject = checkToken(request);
        Integer incomingId = jsonObject.getInteger("incomingId");
        return iCreditService.goContract(incomingId);
    }


    @CheckH5Url
//    @CheckSign
    @ApiOperation("保单贷--签署合同")
    @PostMapping("/goContractInsurance")
    public void goContractInsurance(@RequestBody List<AddSignerVo> list, HttpServletRequest request) {
        JSONObject jsonObject = checkToken(request);
        Integer incomingId = jsonObject.getInteger("incomingId");
        String payPass = jsonObject.getString("payPass");
        Integer outId = iCreditService.goContractInsurance(incomingId,payPass);
//        List<AddSignerVo> list = JSONObject.parseArray(RSAUtils.privateDecrypt(dto.getData(), privateKey), AddSignerVo.class);
        if (outId == null)
            return;
        apiContractService.addAllSigner(list, incomingId,outId);
    }

    @CheckH5Url
    @ApiOperation("保单贷--预付款")
    @PostMapping("/advancePaymentInsurance")
    public AdvanceRepaymentDetail advancePaymentInsurance(HttpServletRequest request) {
        JSONObject jsonObject = checkToken(request);
        Integer incomingId = jsonObject.getInteger("incomingId");
        return iCreditService.advancePaymentInsurance(incomingId);
    }

    @CheckH5Url
    @CheckSign
    @ApiOperation("H5预绑卡判断")
    @PostMapping("/whetherReadySign")
    public Boolean whetherReadySign(@RequestBody InsuranceDto dto, HttpServletRequest request) {
        JSONObject jsonObject = checkToken(request);
        Integer incomingId = jsonObject.getInteger("incomingId");
        ReadySignDto readySignDto = JSONObject.parseObject(RSAUtils.privateDecrypt(dto.getData(), privateKey), ReadySignDto.class);
        readySignDto.setIncomingId(incomingId);
        return dlApiService.whetherReadySign(readySignDto);
    }



    @CheckH5Url
    @ApiOperation("信息授权申请详情")
    @PostMapping("/authorizationApplication")
    public IncomingInfoInsuranceVo authorizationApplication(HttpServletRequest request) {
        JSONObject jsonObject = checkToken(request);
        Integer incomingId = jsonObject.getInteger("incomingId");
        return iCreditService.authorizationApplication(incomingId);
    }

    @CheckH5Url
    @CheckSign
    @ApiOperation("H5预绑卡")
    @PostMapping("/readySign")
    public String readySign(@RequestBody InsuranceDto dto, HttpServletRequest request) throws Exception {
        if (!active.equals("prd"))
            return "ceshi";
        JSONObject jsonObject = checkToken(request);
        Integer incomingId = jsonObject.getInteger("incomingId");
        ReadySignDto readySignDto = JSONObject.parseObject(RSAUtils.privateDecrypt(dto.getData(), privateKey), ReadySignDto.class);
        readySignDto.setIncomingId(incomingId);
        return dlApiService.readySign(readySignDto);
    }


    @CheckH5Url
    @CheckSign
    @ApiOperation("H5确认绑卡")
    @PostMapping("/confirmSign")
    public void confirmSign(@RequestBody InsuranceDto dto, HttpServletRequest request) throws Exception {
        if (!active.equals("prd"))
            return;
        JSONObject jsonObject = checkToken(request);
        Integer incomingId = jsonObject.getInteger("incomingId");
        Credit one = iCreditService.getOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));
        ConfirmSignDto confirmSignDto = JSONObject.parseObject(RSAUtils.privateDecrypt(dto.getData(), privateKey), ConfirmSignDto.class);
        confirmSignDto.setCreditId(one.getCreditId());
        dlApiService.confirmSign(confirmSignDto);
        one.setRepayAccount(confirmSignDto.getRepayAccount());
        one.setRepayName(confirmSignDto.getRepayName());
        one.setRepayBank(confirmSignDto.getRepayBank());
        LambdaQueryWrapper<Credit> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Credit::getIncomingId, incomingId);
        iCreditService.update(one, lqw);
    }

    private JSONObject checkToken(HttpServletRequest request) {
        String token = request.getHeader("token");
        Integer incomingId = null;
        String payPass = null;
        JSONObject jsonObject = null;
        try {
            Claims claims = JwtUtil.parseJWT(token);
            String subject = claims.getSubject();
            jsonObject = JSONObject.parseObject(subject);
            incomingId = jsonObject.getInteger("incomingId");
            payPass = jsonObject.getString("payPass");
            if (incomingId == null)
                throw new XkdException("身份验证失败");
            if (StringUtils.isNotBlank(payPass)){
                if (!payPass.equals("1") && !payPass.equals("2"))
                    throw new XkdException("付款通道不正确");
            }
        } catch (ExpiredJwtException e) {
            throw new XkdException("token已过期");
        } catch (Exception e) {
            throw new XkdException("token验证失败");
        }
        return jsonObject;
    }

    @CheckH5Url
    @CheckSign
    @ApiOperation("H5预付款-代扣/线下转账")
    @PostMapping("/advancePayment")
    public void advancePayment(@RequestBody InsuranceDto dto, HttpServletRequest request) throws Exception {
        JSONObject jsonObject = checkToken(request);
        Integer incomingId = jsonObject.getInteger("incomingId");
        String token = request.getHeader("token");
        AdvancePaymentDto advancePaymentDto = JSONObject.parseObject(RSAUtils.privateDecrypt(dto.getData(), privateKey), AdvancePaymentDto.class);
        advancePaymentDto.setIncomingId(incomingId);
        String lockKey = ADVANCE_LOCK_KEY + advancePaymentDto.getIncomingId();
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                dlApiService.advancePayment(advancePaymentDto);
                //延迟执行
                delayedScheduler.schedule(() -> {
                    redisTemplate.delete(H5_LINK + token);
                }, 5, TimeUnit.SECONDS);
            }else {
                throw new XkdException("服务器繁忙，请勿重复提交");
            }
        }catch (Exception e){
            log.error("预付款代扣失败，请求参数: {}, 锁键: {}, 异常信息: {}", advancePaymentDto, lockKey, e.getMessage(), e);
            throw e;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("手动代扣任务释放锁，锁键: {}", lockKey);
            }
        }


    }

    /*
     * serialNo:返回流水号 用于添加企业
     * faceUrl:人脸识别链接
     * 当认证渠道为H5（faceAuthMode=2），人脸识别完成后会自动跳转至【redirectUrl】，拼接认证结果且带签名（详细参数说明与验证方法见下方示例）。
     * 例：{redirectUrl}?sign=xxxxxx&msg=xxx&result=1
     * */
    /*
     * 返回流水号
     *  当认证渠道为H5（faceAuthMode=2），人脸识别完成后会自动跳转至【redirectUrl】，拼接认证结果且带签名（详细参数说明与验证方法见下方示例）。
     *  例：{redirectUrl}?sign=xxxxxx&msg=xxx&result=1
     *  响应 人脸识别链接
     * */
    @CheckH5Url
    @ApiOperation(value = "人脸识别")
    @GetMapping("face")
    public HashMap<String, String> face(HttpServletRequest request) {
        JSONObject jsonObject = checkToken(request);
        Integer incomingId = jsonObject.getInteger("incomingId");
        IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
        String token = request.getHeader("token");
        if (incomingInfo.getPrincipalType().equals(SysConstant.PRINCIPAL_TYPE_ZERO)) {//个人
            return apiContractService.face(incomingId, token);
        } else {//企业
            return apiContractService.companyFace(incomingId, token);
        }
    }

//    @CheckH5Url
//    @CheckSign
//    @ApiOperation(value = "添加企业生成企业印章")
//    @GetMapping("addCompany")
//    public void addCompany(@RequestBody InsuranceDto dto, HttpServletRequest request) {
//        JSONObject jsonObject = JSONObject.parseObject(RSAUtils.privateDecrypt(dto.getData(), privateKey));
//        String serialNo = jsonObject.getString("serialNo");//流水号
//        Integer incomingId = checkToken(request);
//        apiContractService.addCompany(incomingId, serialNo);
//    }


    /*
     * 认证成功后
     * 代表进件成功
     * 修改进件状态到待授信
     * */
    @CheckH5Url
    @CheckSign
    @ApiOperation(value = "验签认证")
    @PostMapping("sign")
    @Transactional(rollbackFor = Exception.class)
    public void sign(@RequestBody InsuranceDto dto, HttpServletRequest request) throws Exception {
        JSONObject jsonObject = JSONObject.parseObject(RSAUtils.privateDecrypt(dto.getData(), privateKey));
        JSONObject data = checkToken(request);
        Integer incomingId = data.getInteger("incomingId");
        String token = request.getHeader("token");
        String sign = jsonObject.getString("sign");
        String msg = jsonObject.getString("msg");
        String encode = URLEncoder.encode(msg, "UTF-8");
        String result = jsonObject.getString("result");
        String params = "&msg=" + encode + "&result=" + result;
        String lockKey = H5_LINK + incomingId;
        RLock lock = redissonClient.getLock(H5_LINK + incomingId);
        try {
            log.info("尝试获取锁: {}", lockKey);
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                log.info("成功获取锁: {}", lockKey);
                IncomingInfo incomingInfo = incomingInfoMapper.selectById(incomingId);
                if (!incomingInfo.getStatus().equals(SysConstant.BUSINESS_STATUS_TWO))
                    throw new XkdException("认证完成，无需再次认证");
                String name = "";
                String idCard = "";
                if (incomingInfo.getPrincipalType().equals("0")) {//个人
                    name = incomingInfo.getName();
                    idCard = incomingInfo.getIdCard();
                } else if (incomingInfo.getPrincipalType().equals("1")) {//企业
                    name = incomingInfo.getLegalEntityName();
                    idCard = incomingInfo.getLegalIdCard();
                }
                String md5 = MD5Util.MD5a32(name + idCard);
                RsaEncrypt rsaEncrypt = new RsaEncrypt();
                rsaEncrypt.loadPublicKey(aqHtProperties.getAqpublickey());
                byte[] bytes = Base64.getDecoder().decode(sign);
                boolean b = rsaEncrypt.doCheck(md5 + params, bytes, rsaEncrypt.getPublicKey());
                if (!b)
                    throw new XkdException("验签失败");

                incomingInfoMapper.updateStatus(SysConstant.BUSINESS_STATUS_THREE, incomingId);

                String xxsqsname = "";
                String xxsqsidcard = "";
                if (incomingInfo.getPrincipalType().equals("0")) {//个人
                    xxsqsname = incomingInfo.getName();
                    xxsqsidcard = incomingInfo.getIdCard();
                    String getSerialNo = (String) redisTemplate.opsForValue().get(serialNo + token);
                    if (getSerialNo == null)
                        throw new XkdException("流水号失效");
                    apiContractService.addPersonalUser(incomingId, getSerialNo);//添加个人用户
                } else if (incomingInfo.getPrincipalType().equals("1")) { //企业
                    xxsqsname = incomingInfo.getLegalEntityName();
                    xxsqsidcard = incomingInfo.getLegalIdCard();
                    if (result.equals("1")) {//认证成功 生成签章
                        String getSerialNo = (String) redisTemplate.opsForValue().get(serialNo + token);
                        if (getSerialNo == null)
                            throw new XkdException("流水号失效");
                        apiContractService.addCompany(incomingId, getSerialNo, incomingInfo.getUnifiedSocialCreditCode());//添加企业
                    }
                }
                //生产刘总说的临时授权书
                String contractNo = OrderNumUtil.getContractNo();
                String filePath = contractUrl + "/" + incomingInfo.getCompanyId() + "/" + contractNo + ".pdf";
                String fileUrl = localPathDir + filePath;//文件下载路径
                Contract c = new Contract();
                //第一次签约
                //保存签约合同信息
                c.setIncomingId(incomingInfo.getIncomingId());
                c.setContractNo(contractNo);
                c.setType(SysConstant.CONTRACT_TYPE_ONE);
                c.setStatus(SysConstant.CONTRACT_DO);
                c.setStartTime(new Date());
                c.setContractTime(new Date());
                c.setContractName("个人信息授权书");
                c.setCompanyId(incomingInfo.getCompanyId());
                c.setContract(filePath);
                contractMapper.insert(c);

                Map<String, Object> map = Maps.newHashMap();
                map.put("name", xxsqsname);
                map.put("time", new SimpleDateFormat("yyyy年MM月dd日").format(new Date()));
                map.put("idcard", xxsqsidcard);
                PdfUtil.Pdf2(map, "/个人信息授权使用书模板.pdf", fileUrl);
                //延迟执行
                delayedScheduler.schedule(() -> {
                    redisTemplate.delete(H5_LINK + token);
                    redisTemplate.delete(serialNo + token);
                }, 5, TimeUnit.SECONDS);
            } else {
                throw new XkdException("服务器繁忙请稍后再试");
            }
        } catch (Exception e) {
            log.error("验签认证, 锁键: {}, 异常信息: {}", lockKey, e.getMessage(), e);
            throw e;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("验签认证，锁键: {}", lockKey);
            }
        }


    }

    public static void main(String[] args) throws Exception {

        String pub = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5Rn9gaLYcXH3dDyNrEWbwwsXxAcVmX+xbgD15Q42UUGV5kXX84fRlJgjTRd8Hp3EHNmrcXv8owX8JzU5oMFzKNsz9aGjHreH99r4t3yUczt2g/duuwJL2W4eCBFBwCU59gdG3/ndny4X0VfuU7T4jzpa4nzU3wZjL9bcoiubUmTkELm7CzS86IJ/FPKGxSQQYM7ltR0F411pbSJncajxjxO6GafjsqQmBdF0+UEEdft988MaTP5MPeYLcuqYGqlnYfKzkCU4ra/qhJ9cRNs7mVidTjLQq5DAxv3lnqqe9xBzC1mznet7wT9wGN/xERs4SfDyyzEunBtcuaBZ6+bU3wIDAQAB";
        String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDlGf2Bothxcfd0PI2sRZvDCxfEBxWZf7FuAPXlDjZRQZXmRdfzh9GUmCNNF3wencQc2atxe/yjBfwnNTmgwXMo2zP1oaMet4f32vi3fJRzO3aD9267AkvZbh4IEUHAJTn2B0bf+d2fLhfRV+5TtPiPOlrifNTfBmMv1tyiK5tSZOQQubsLNLzogn8U8obFJBBgzuW1HQXjXWltImdxqPGPE7oZp+OypCYF0XT5QQR1+33zwxpM/kw95gty6pgaqWdh8rOQJTitr+qEn1xE2zuZWJ1OMtCrkMDG/eWeqp73EHMLWbOd63vBP3AY3/ERGzhJ8PLLMS6cG1y5oFnr5tTfAgMBAAECggEAT4F7A/uXAJur5BdFlTTc7s2RNFK/ELOLSCEmNJT/jahklHLFlDqcpWQ8z/qn9NoMXnS9thbwoESp8pNaierIg7O6rvImsTjBA2dm7ApcXDcVFIwQTe7okq7P1j3i4wMoP7TbyEumuSCeXGSXDU6e+dmnA7WNjE5jqoz2qYLXlrpLceaYzHI2yrpP61xr3rbURzR0sdfCmjI5g4dVUWPxeD8e3JiQ+gONziVDi71cioMEfLmDB7yLlZ5MKwxpkC/0U0Z3yXpKtB+j7mU/JaPz5J4FBubiB6Sdj/PROuxlCv/EkSbpuVMmZKyVvMx4QnuvG3GOL8zms77RmLB2ewS1eQKBgQD4/RLPpauznK+++BHvtfo6mEhxq3ZT/DS4l71MEDfhK5XE6mChfAjVDG6z7pHGy4rToVUbg6yAnofOs4AtOja7t/zX3NLVaIfC3J4lzDgRO1tjRyvpz01pyyRNfKbIIuk9ZYNt8+TFkSi3fmFIloDYog6wdqoU9O7Aj8j5sfromQKBgQDrjY27aAXRNsjglwXvTIAj6Rpblh1lMx8r5dYEanueOQ/IfY2aGsanX2i2Qiv+QbmnlYVWVo75PEh/sb0hc/Z3bCI6f7IJ9e2J8tUWw7PHZ2fgE4iJmUy4JP3ru9wvTrLvSaUvrykNGNzA/wIX5YeZAlW8+g6TsyUNrJKyGjM8NwKBgQD4JSgxQDhuKjNRDOS7VU7MROa2Op8UNp/IdJnZC7TVOV3rQXcKmZYn1lsiAhrQ2BxErF4orMMn8FcDHwwYhxLzZPudmtuYirP69Wq72trp+ZEr0Uunms3ZKPbGLvv9LCXt5ZTl6RNP/0poDZEhCbXhsSkYnzsE5dJooSQ4DjQ0yQKBgQCM9Q5loWApzHD1BSsGAm+EDdA6EmDQ2IDE0ir6RYZoLpllTszX9UH/krakqoCNDGO/B1jdS7sPJqXz1Dbrt+s7l7f77cJq94IIVQpzLvV8VELy8n14agZhA7H3yVl/7cZiL13sZRNZxREFZRSpnC9CvwTzq2fhtwhrYGQAtHEIjQKBgQC4Pb0KOMEyWpl3iYHg/2DX5cH+TPqc1VO6M2QJNxw3HbBv8vr03F1cG35kuhWWZoUxREEQpr4t/WNcBy2SDhcsaPwCsCyaqM5soBrWDxI+3oBIm311XtLXXbcehqZX2wPQ2cuoI0s1AYwfww0IJfJ99jBiD2T+knHl/NvkWh//7A==";

        String data = "N0576UzGvTexayL2aN7Mbt2LTOS8L07gOlLp9mdBEzqQv2f6LSC6KGuZjvAoHvMOqKWaa844CKGRZujiyW0fFu8FaRfp+59/zhBDFviosSBROQ9EDRjxwbh21niwlG7HgCT8USFS8nkeWwb4HG53vBjfYeCC3BqFa7QvsUR4mTfmrYVPBpiFFYqjUMzOGZrvd+IXIJ/Qzo+LLVi5jgRvVF/Qv/ORSqTiQC+exiVqeAFpcER4Wapfb2dSjbWXRNBnTHCkvL0kHLkZ2oPk26WBHDpy1GnMwYi+9wiK1PkYCPZb1hI7cSw7V1L8L2TSFoKBBxK8yWqS92C7aLDvXSas8w==";

        String s2 = RSAUtils.privateDecrypt(data, privateKey);
        System.out.println(s2);

    }


}
